import Publisher from "./Publisher";
import { Object3D } from "three";

/**
 * 这个类是大部分应用中的对象的基类。它负责管理某个由应用层自定义的对象，同时还会处理一些基本的Three.js操作
 * 例如在场景中增加或移除物体，增加或移除某个对象的子对象，设置或恢复到Three.js的默认设置，例如位置、缩放和旋转
 */
export default class SimObject extends Publisher {
    public object3D: any;
    public children: any[];

    public constructor() {
        super();
        this.object3D = null;
        this.children = [];
    }

    public update(): void {
        this.updateChildren();
    }

    public setPosition(x: number, y: number, z: number): void {
        if (this.object3D) {
            this.object3D.position.set(x, y, z);
        }
    }

    public setScale(x: number, y: number, z: number): void {
        if (this.object3D) {
            this.object3D.scale.set(x, y, z);
        }
    }

    public setVisible(visible: boolean): void {
        function setVisible(obj: any, visible: boolean): void {
            obj.visible = visible;
            let i: number, len: number = obj.children.length;
            for (i = 0; i < len; i++) {
                setVisible(obj.children[i], visible);
            }
        }
        if (this.object3D) {
            setVisible(this.object3D, visible);
        }
    }

    private updateChildren(): void {
        let i: number, len: number;
        len = this.children.length;
        for (i = 0; i < len; i++) {
            this.children[i].update();
        }
    }

    public setObject3D(object3D: Object3D): void {
        object3D.userData = this;
        this.object3D = object3D;
    }

    public addChild(child: any): void {
        this.children.push(child);
        if (child.object3D) {
            this.object3D.add(child.object3D);
        }
    }

    public removeChild(child: any): void {
        let index: number = this.children.indexOf(child);
        if (index !== -1) {
            this.children.splice(index, 1);
            if (child.object3D) {
                this.object3D.remove(child.object3D);
            }
        }
    }

    public getScene(): Object3D {
        let scene: any = null;
        if (this.object3D) {
            let obj: Object3D = this.object3D;
            while (obj.parent) {
                obj = obj.parent;
            }
            scene = obj;
        }
        return scene;
    }

    public getApp(): any {
        let scene: Object3D = this.getScene() as Object3D;
        return scene ? scene.userData : null;
    }
}